<?php
/**
 * ThemeFrameworks for XOOPS Themes
 *
 * You may not change or alter any portion of this comment or credits
 * of supporting developers from this source code or any supporting source code 
 * which is considered copyrighted (c) material of the original comment or credit authors.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @copyright       The Project http://www.ncsoft.com.cn
 * @license         http://www.ncsoft.com.cn
 * @package         ThemeFrameworks
 * @since           2.3.0
 * @author          Hui Xiao <xiaohui@ncsoft.com.cn>
 * @version         $Id: xoTheme.php Fri Jul 31 10:19:23 CST 2009
 */

include XOOPS_ROOT_PATH . '/class/template.php';

class xoGroupTheme
{
	
	public $xoBundleIdentifier = 'xos_opal_ThemeFactory';
    /**
     * Currently enabled themes (if empty, all the themes in themes/ are allowed)
     * @var array
     */
    public $allowedThemes = array();
    
    public $themePath = XOOPS_THEME_PATH;
    /**
     * Default theme to instanciate if none specified
     * @var string
     */
    public $defaultTheme = 'default';
    /**
     * If users are allowed to choose a custom theme
     * @var bool
     */
    public $allowUserSelection = true;

    /**
    * The name of this theme
    * @var string
    */
    public $folderName = '';
   /**
    * Physical path of this theme folder
    * @var string
    */
    public $path = '';
    public $url = '';

   /**
    * Whether or not the theme engine should include the output generated by php
    * @var string
    */
    public $bufferOutput = true;
    /**
    * Canvas-level template to use
    * @var string
    */
    public $canvasTemplate = 'theme.html';
    /**
    * Content-level template to use
    * @var string
    */
    public $contentTemplate = '';

    public $contentCacheLifetime = 0;
    public $contentCacheId = null;

    /**
    * Text content to display right after the contentTemplate output
    * @var string
    */
    public $content = '';
    /**
    * Page construction plug-ins to use
    * @var array
    * @access public
    */
    public $plugins = array( 'xos_logos_PageBuilder' );

    public $renderCount = 0;
    /**
     * Pointer to the theme template engine
     * @var XoopsTpl
     */
    public $template = false;

    /**
     * Array containing the document meta-information
     * @var array
     */
    public $metas = array(
        'http' => array(
            'Content-Script-Type' => 'text/javascript',
            'Content-Style-Type' => 'text/css',
        ),
        'meta' => array(),
        'link' => array(),
        'script' => array(),
    );

    /**
     * Array of strings to be inserted in the head tag of HTML documents
     * @var array
     */
    public $htmlHeadStrings = array();
    /**
     * Custom variables that will always be assigned to the template
     * @var array
     */
    public $templateVars = array();

    /**
     * User extra information for cache id, like language, user groups
     *
     * @var boolean
     */
    public $use_extra_cache_id = true;
    
    public function __construct( $options = array(), $initArgs = array() ) {
    	if ( @empty( $options['folderName'] ) ) {
            if ( ( $req = @$_REQUEST['xoops_theme_select'] ) && $this->isThemeAllowed( $req ) ) {
                $options['folderName'] = $req;
                if ( isset( $_SESSION ) && $this->allowUserSelection ) {
                    $_SESSION[ $this->xoBundleIdentifier ]['defaultTheme'] = $req;
                }
            } elseif ( isset( $_SESSION[ $this->xoBundleIdentifier ]['defaultTheme'] ) ) {
                $options['folderName'] = $_SESSION[ $this->xoBundleIdentifier ]['defaultTheme'];
            } elseif ( @empty( $options['folderName'] ) || !$this->isThemeAllowed( $options['folderName'] ) ) {
                $options['folderName'] = $this->defaultTheme;
            }
            $GLOBALS['xoopsConfig']['theme_set'] = $options['folderName'];
        }
        if ( !empty($options['themePath']) ) {
        	$this->themePath = $options['themePath'] ;
        }
        $options['path'] = $this->themePath . '/' . $options['folderName'];
        foreach ( $options as $k => $v ) $this->$k = $v;
        $inst = $this->xoInit();
        return $inst;
    }
    /**
     * Checks if the specified theme is enabled or not
     * @param string $name
     * @return bool
     */
    public function isThemeAllowed( $name ) {
        return ( empty( $this->allowedThemes ) || in_array( $name, $this->allowedThemes ) );
    }
	
    public function getTemplate() {
    	return $this->template;
    }
    
    public function xoInit( $options = array() ) {
        global $xoops;

        $this->url = $this->themePath . '/' . $this->folderName;

        $this->template = new XoopsTpl();
        $this->template->currentTheme = $this;
        $this->template->assign_by_ref( 'xoTheme', $this );

        // Meta tags
        $config_handler = xoops_gethandler('config');
        $criteria = new CriteriaCompo(new Criteria('conf_modid', 0));
        $criteria->add(new Criteria('conf_catid', XOOPS_CONF_METAFOOTER));
        $config = $config_handler->getConfigs($criteria, true);
        foreach ( array_keys($config) as $i ) {
            $name = $config[$i]->getVar( 'conf_name', 'n' );
            $value = $config[$i]->getVar( 'conf_value', 'n' );
            if ( substr( $name, 0, 5 ) == 'meta_' ) {
                $this->addMeta( 'meta', substr( $name, 5 ), $value );
            } else {
                // prefix each tag with 'xoops_'
                $this->template->assign( "xoops_$name", $value );
            }
        }

        $this->loadLocalization();

        if ( $this->bufferOutput ) {
            ob_start();
        }
        $GLOBALS['xoTheme'] = $this;
        $GLOBALS['xoopsTpl'] = $this->template;
        // Instanciate and initialize all the theme plugins
//        foreach ( $this->plugins as $k => $bundleId ) {
//            if ( !is_object( $bundleId ) ) {
//                $this->plugins[$bundleId] = new $bundleId();
//                $this->plugins[$bundleId]->theme = $this;
//                $this->plugins[$bundleId]->xoInit();
//                unset( $this->plugins[$k] );
//            }
//        }
        return true;
    }
    /**#@-*/

    /**
     * Generate cache id based on extra information of language and user groups
     *
     * User groups other than anonymous should be detected to avoid disclosing group sensitive contents
     *
     * @param string    $cache_id        raw cache id
     * @param string    $extraString    extra string
     *
     * @return string    complete cache id
     */
    public function generateCacheId($cache_id, $extraString = '') {
        static $extra_string;

        if (!$this->use_extra_cache_id) {
            return $cache_id;
        }

        if (empty($extraString)) {
            if (empty($extra_string)) {
                global $xoopsUser, $xoopsConfig;

                // Generate language section
                $extra_string = $xoopsConfig['language'];

                // Generate group section
                if ( !@is_object( $xoopsUser ) ) {
                    $extra_string .= '|' . XOOPS_GROUP_ANONYMOUS;
                } else {
                    $groups = $xoopsUser->getGroups();
                    sort($groups);
                    // Generate group string for non-anonymous groups,
                    // XOOPS_DB_PASS and XOOPS_DB_NAME (before we find better variables) are used to protect group sensitive contents
                    $extra_string .= '|' . implode(",", $groups).substr( md5(XOOPS_DB_PASS.XOOPS_DB_NAME), 0, strlen(XOOPS_DB_USER) * 2 );
                }
            }
            $extraString = $extra_string;
        }
        $cache_id .= '|' . $extraString;

        return $cache_id;
    }

    public function checkCache() {
        global $xoopsModule, $xoopsLogger;

        if ( $_SERVER['REQUEST_METHOD'] != 'POST' && $this->contentCacheLifetime ) {
            $template = $this->contentTemplate ? $this->contentTemplate : 'db:system_dummy.html';

            $this->template->caching = 2;
            $this->template->cache_lifetime = $this->contentCacheLifetime;
            $uri = str_replace( XOOPS_URL, '', $_SERVER['REQUEST_URI'] );
            // Clean uri by removing session id
            if (defined('SID') && SID && strpos($uri, SID)) {
                $uri = preg_replace("/([\?&])(".SID."$|".SID."&)/", "\\1", $uri);
            }
            $this->contentCacheId = $this->generateCacheId($uri);

            if ( $this->template->is_cached( $template, $this->contentCacheId ) ) {
                $xoopsLogger->addExtra( $template,    sprintf('Cached (regenerates every %d seconds)', $this->contentCacheLifetime  ) );
                $this->render( null, null, $template );
                return true;
            }
        }
        return false;
    }


    /**
     * Render the page
     *
     * The theme engine builds pages from 2 templates: canvas and content.
     *
     * A module can call this method directly and specify what templates the theme engine must use.
     * If render() hasn't been called before, the theme defaults will be used for the canvas and
     * page template (and xoopsOption['template_main'] for the content).
     *
     * @param string    $canvasTpl      The canvas template, if different from the theme default
     * @param string    $pageTpl        The page template, if different from the theme default (unsupported, 2.3+ only)
     * @param string    $contentTpl     The content template
     * @param array     $vars           Template variables to send to the template engine
     */
    public function render( $canvasTpl = null, $pageTpl = null, $contentTpl = null, $vars = array() ) {
        global $xoops, $xoopsLogger, $xoopsOption;

        if ( $this->renderCount ) {
            return false;
        }
        $xoopsLogger->startTime( 'Page rendering' );

        // @internal: Lame fix to ensure the metas specified in the xoops config page don't appear twice
        $old = array( 'robots', 'keywords', 'description', 'rating', 'author', 'copyright' );
        foreach ( $this->metas['meta'] as $name => $value ) {
            if ( in_array( $name, $old ) ) {
                $this->template->assign( "xoops_meta_$name", htmlspecialchars( $value, ENT_QUOTES ) );
                unset( $this->metas['meta'][$name] );
            }
        }

        if ( $canvasTpl )        $this->canvasTemplate    = $canvasTpl;
        if ( $contentTpl )        $this->contentTemplate    = $contentTpl;

        if ( !empty( $vars ) ) {
            $this->template->assign( $vars );
        }
        if ( $this->contentTemplate ) {
            $this->content = $this->template->fetch( $this->contentTemplate, $this->contentCacheId );
        }
        if ( $this->bufferOutput ) {
            $this->content .= ob_get_contents();
            ob_end_clean();
        }
        $this->template->assign_by_ref( 'xoops_contents', $this->content );

        // We assume no overlap between $xoopsOption['xoops_module_header'] and $this->template->get_template_vars( 'xoops_module_header' ) ?
        $header = empty($xoopsOption['xoops_module_header']) ? $this->template->get_template_vars( 'xoops_module_header' ) : $xoopsOption['xoops_module_header'];
        $this->template->assign( 'xoops_module_header', $this->renderMetas( null, true ) . "\n" . $header );

		if ( !empty($xoopsOption['xoops_pagetitle']) ) {
            $this->template->assign( 'xoops_pagetitle', $xoopsOption['xoops_pagetitle'] );
        }

        // Do not cache the main (theme.html) template output
        $this->template->caching = 0;
        $this->template->display( $this->path . '/' . $this->canvasTemplate );

        $this->renderCount++;
        $xoopsLogger->stopTime( 'Page rendering' );
    }

    /**
     * Load localization information
     *
     * Folder structure for localization:
     *  <ul>themes/themefolder/english
     *      <li>main.php - language definitions</li>
     *      <li>style.css - localization stylesheet</li>
     *      <li>script.js - localization script</li>
     *  </ul>
     */
    public function loadLocalization() {
        global $xoops;
        $language = $GLOBALS["xoopsConfig"]["language"];

        if ( !file_exists( "{$this->path}/language/{$language}" ) ) {
            return true;
        }

        $this->addLanguage();
        $this->addScript( "language/{$language}/script.js" );
        $this->addStylesheet( "language/{$language}/style.css" );

        return true;
    }

    /**
     * Load theme specific language constants
     *
     * @param string    $type       language type, like 'main', 'admin'; Needs to be declared in theme xo-info.php
     * @param string    $language   specific language
     */
    public function addLanguage($type = "main", $language = null){
        global $xoops;
        $language = is_null($language) ? $GLOBALS["xoopsConfig"]["language"] : $language;
        if (false === @include $xoops->path( $this->resourcePath( "/language/{$language}/{$type}.php" ) ) ) {
            include $xoops->path( $this->resourcePath( "/language/english/{$type}.php" ) );
        }
        return true;
    }

    /**#@+ @tasktype 20 Manipulating page meta-information*/
    /**
    * Adds script code to the document head
    *
    * This methods allows the insertion of an external script file (if $src is provided), or
    * of a script snippet. The file URI is parsed to take benefit of the theme resource
    * overloading system.
    *
    * The $attributes parameter allows you to specify the attributes that will be added to the
    * inserted <script> tag. If unspecified, the <var>type</var> attribute value will default to
    * 'text/javascript'.
    *
    * <code>
    * // Add an external script using a physical path
    * $theme->addScript( 'www/script.js', null, '' );
    * $theme->addScript( 'modules/newbb/script.js', null, '' );
    * // Specify attributes for the <script> tag
    * $theme->addScript( 'mod_xoops_SiteManager#common.js', array( 'type' => 'application/x-javascript' ), '' );
    * // Insert a code snippet
    * $theme->addScript( null, array( 'type' => 'application/x-javascript' ), 'window.open("Hello world");' );
    * </code>
    *
    * @param string $src path to an external script file
    * @param array $attributes hash of attributes to add to the <script> tag
    * @param string $content Code snippet to output within the <script> tag
    *
    * @return void
    **/
    public function addScript( $src = '', $attributes = array(), $content = '' ) {
        global $xoops;
        if ( empty( $attributes ) )             $attributes = array();
        if ( !empty( $src ) )                   $attributes['src'] = $xoops->url( $this->resourcePath( $src ) );
        if ( !empty( $content ) )               $attributes['_'] = $content;
        if ( !isset( $attributes['type'] ) )    $attributes['type'] = 'text/javascript';
        $this->addMeta( 'script', $src, $attributes );
    }

    /**
     * Add StyleSheet or CSS code to the document head
     * @param string $src path to .css file
     * @param array $attributes name => value paired array of attributes such as title
     * @param string $content CSS code to output between the <style> tags (in case $src is empty)
     *
     * @return void
     **/
    public function addStylesheet( $src = '', $attributes = array(), $content = '' ){
        global $xoops;
        if ( empty( $attributes ) )             $attributes = array();
        if ( !empty( $src ) )                     $attributes['href'] = $xoops->url( $this->resourcePath( $src ) );
        if ( !isset($attributes['type']) )         $attributes['type'] = 'text/css';
        if ( !empty( $content ) )                 $attributes['_'] = $content;
        $this->addMeta( 'stylesheet', $src, $attributes );
    }

    /**
      * Add a <link> to the header
      * @param string    $rel        Relationship from the current doc to the anchored one
      * @param string    $href        URI of the anchored document
      * @param array        $attributes    Additional attributes to add to the <link> element
      */
    public function addLink( $rel, $href = '', $attributes = array() ) {
        global $xoops;
        if ( empty( $attributes ) )             $attributes = array();
        if ( !empty( $href ) )                     $attributes['href'] = $href;
        $this->addMeta( 'link', $rel, $attributes );
    }

    /**
     * Set a meta http-equiv value
     */
    public function addHttpMeta( $name, $value = null ) {
        if ( isset($value) ) {
            return $this->addMeta( 'http', $name, $value );
        }
        unset( $this->metas['http'][$name] );
    }

    /**
     * Change output page meta-information
     */
    public function addMeta( $type = 'meta', $name = '', $value = '' ) {
        if ( !isset( $this->metas[$type] ) ) {
            $this->metas[$type] = array();
        }
        if ( isset($name) ) {
            $this->metas[$type][$name] = $value;
        } else {
            $this->metas[$type][] =     $value;
        }
        return $value;
    }

    public function headContent( $params, $content, &$smarty, &$repeat ) {
        if ( !$repeat ) {
            $this->htmlHeadStrings[] = $content;
        }
    }

    public function renderMetas( $type = null, $return = false ) {
        $str = '';
        if ( !isset($type) ) {
            foreach ( array_keys($this->metas) as $type ) {
                $str .= $this->renderMetas( $type, true );
            }
            $str .= implode( "\n", $this->htmlHeadStrings );
        } else {
            switch ( $type ) {
            case 'script':
                foreach ( $this->metas[$type] as $attrs ) {
                    $str .= '<script' . $this->renderAttributes( $attrs ) . ">\n";
                    if ( @$attrs['_'] ) {
                        $str .= "\n//<![CDATA[\n" . $attrs['_'] . "\n//]]>";
                    }
                    $str .= "</script>\n";
                }
                break;
            case 'link':
                foreach ( $this->metas[$type] as $rel => $attrs ) {
                    $str .= '<link rel="' . $rel . '"' . $this->renderAttributes( $attrs ) . " />\n";
                }
                break;
            case 'stylesheet':
                foreach ( $this->metas[$type] as $attrs ) {
                    if ( @$attrs['_'] ) {
                        $str .= '<style' . $this->renderAttributes($attrs) . ">\n/* <![CDATA[ */\n" . $attrs['_'] . "\n/* //]]> */\n</style>";
                    } else {
                        $str .= '<link rel="stylesheet"' . $this->renderAttributes($attrs) . " />\n";
                    }
                }
                break;
            case 'http':
                foreach ( $this->metas[$type] as $name => $content ) {
                    $str .= '<meta http-equiv="' . htmlspecialchars( $name, ENT_QUOTES ) . '" content="' . htmlspecialchars( $content, ENT_QUOTES) . "\" />\n";
                }
                break;
            default:
                foreach ( $this->metas[$type] as $name => $content ) {
                    $str .= '<meta name="' . htmlspecialchars( $name, ENT_QUOTES ) . '" content="' . htmlspecialchars( $content, ENT_QUOTES) . "\" />\n";
                }
                break;
            }
        }
        if ( $return ) {
            return $str;
        }
        echo $str;
        return true;
    }

    /**
     * Generates a unique element ID
     * @param string $tagName
     * @return string
     */
    public function genElementId( $tagName = 'xos' ) {
        static $cache = array();
        if ( !isset( $cache[ $tagName ] ) ) {
            $cache[$tagName] = 1;
        }
        return $tagName . '-' . $cache[$tagName]++;
    }

    /**
     * Transform an attributes collection to an XML string
     * @param array $coll
     * @return string
     */
    public function renderAttributes( $coll ) {
        $str = '';
        foreach ( $coll as $name => $val ) {
            if ( $name != '_' ) {
                $str .= ' ' . $name . '="' . htmlspecialchars( $val, ENT_QUOTES ) . '"';
            }
        }
        return $str;
    }

    /**
     * Return a themable file resource path
     *
     * @param string $path
     * @return string
     */
    public function resourcePath( $path ) 
	{
        global $xoops;
        if ( substr( $path, 0, 1 ) == '/' ) {
            $path = substr( $path, 1 );
        }
        if ( file_exists( "$this->path/$path" ) ) {
            return "themes/$this->folderName/$path";
        }
        return $path;
    }
	
	public function getDefAssign() 
	{
		global $xoopsModule, $xoopsConfig;
		$this->template->assign(array(
			'xoops_url' 			=> XOOPS_URL,
			'xoops_langcode' 	=> _LANGCODE,
			'xoops_charset' 	=> _CHARSET,
			'xoops_version' 	=> XOOPS_VERSION,
			'xoops_requesturi' => htmlspecialchars( $_SERVER['REQUEST_URI'], ENT_QUOTES),
			'xoops_sitename' 	=> htmlspecialchars($xoopsConfig['sitename'], ENT_QUOTES),
			'xoops_slogan' 		=> htmlspecialchars($xoopsConfig['slogan'], ENT_QUOTES),
			'xoops_dirname' 	=> $xoopsModule->getVar( 'dirname' ),
			'xoops_pagetitle' 	=> $xoopsModule->getVar('name','n'),
			'xoops_upload_url' 	=> XOOPS_UPLOAD_URL,
		));
		return ;
	}
}